#region Copyright (c) 2006-2009 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2009 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Xml;
using System.Collections;
using System.ComponentModel;
using Widgetsphere.Generator.Common.Util;
using Widgetsphere.Generator.Common.GeneratorFramework;
using System.Text;
using System.Collections.Generic;

namespace Widgetsphere.Generator.Models
{
	public class Relation : BaseModelObject
	{
		#region Member Variables

		protected int _id = 1;
		protected Reference _parentTableRef = null;
		protected Reference _childTableRef = null;
		protected string _roleName = "";
    protected string _constraintName = "";
		protected ColumnRelationshipCollection _columnRelationships = null;
		private DateTime _createdDate = DateTime.Now;

		#endregion

		#region Constructor

    public Relation(IModelObject root)
      : base(root)
		{
			_columnRelationships = new ColumnRelationshipCollection(this.Root);
		}

		#endregion

    #region Events

    public event System.EventHandler BeforeChildTableChange;
    public event System.EventHandler BeforeParentTableChange;
    public event System.EventHandler AfterChildTableChange;
    public event System.EventHandler AfterParentTableChange;

    protected virtual void OnBeforeChildTableChange(object sender, System.EventArgs e)
    {
      if (this.BeforeChildTableChange != null)
        this.BeforeChildTableChange(sender, e);
    }

    protected virtual void OnBeforeParentTableChange(object sender, System.EventArgs e)
    {
      if (this.BeforeParentTableChange != null)
        this.BeforeParentTableChange(sender, e);
    }

    protected virtual void OnAfterChildTableChange(object sender, System.EventArgs e)
    {
      if (this.AfterChildTableChange != null)
        this.AfterChildTableChange(sender, e);
    }

    protected virtual void OnAfterParentTableChange(object sender, System.EventArgs e)
    {
      if (this.AfterParentTableChange != null)
        this.AfterParentTableChange(sender, e);
    }

    #endregion

    #region Property Implementations

    /// <summary>
    /// Determines the field mappings of this relationship.
    /// </summary>
		[
		Description("Determines the field mappings of this relationship."),
		Category("Data"),
		Editor(typeof(Widgetsphere.Generator.Design.Editors.ColumnRelationshipCollectionEditor), typeof(System.Drawing.Design.UITypeEditor)),
		TypeConverter(typeof(Widgetsphere.Generator.Design.Converters.ColumnRelationshipCollectionConverter)),
		]
		public ColumnRelationshipCollection ColumnRelationships
		{
      get { return _columnRelationships; }
		}

    /// <summary>
    /// Determines the parent table in the relationship.
    /// </summary>
		[
    Browsable(false),
		Description("Determines the parent table in the relationship."),
		Category("Data"),
		Editor(typeof(Widgetsphere.Generator.Design.Editors.TableReferenceSingleSelectEditor), typeof(System.Drawing.Design.UITypeEditor)),
		]
		public Reference ParentTableRef
		{
      get { return _parentTableRef; }
			set
			{
        if (_parentTableRef != value)
        {
          this.OnBeforeParentTableChange(this, new EventArgs());
          _parentTableRef = value;
          this.RefreshRoleName();
          this.OnAfterParentTableChange(this, new EventArgs());
          this.OnPropertyChanged(this, new PropertyChangedEventArgs("parentTableRef"));
        }
			}
		}

    /// <summary>
    /// Determines the child table in the relationship.
    /// </summary>
		[
		Description("Determines the child table in the relationship."),
		Category("Data"),
		Editor(typeof(Widgetsphere.Generator.Design.Editors.TableReferenceSingleSelectEditor), typeof(System.Drawing.Design.UITypeEditor)),
		]
		public Reference ChildTableRef
		{
      get { return _childTableRef; }
			set
			{
        if (_childTableRef != value)
        {
          this.OnBeforeChildTableChange(this, new EventArgs());
          _childTableRef = value;
          this.RefreshRoleName();
          this.OnAfterChildTableChange(this, new EventArgs());
          this.OnPropertyChanged(this, new PropertyChangedEventArgs("childTableRef"));
        }
			}
		}

    /// <summary>
    /// Determines the unique id of this object.
    /// </summary>
		[Browsable(false)]
		public int Id
		{
      get { return _id; }
		}

    /// <summary>
    /// Determines the database role name of this relation.
    /// </summary>
		[
		Description("Determines the database role name of this relation."),
		Category("Data"),
		]
		public string RoleName
		{
      get { return _roleName; }
			set
			{
        if (_roleName != value)
        {
          _roleName = value;
          this.OnPropertyChanged(this, new PropertyChangedEventArgs("RoleName"));
        }
			}
		}

    [Browsable(false)]		
    public string ConstraintName
    {
      get { return _constraintName; }
      set
      {
        if (_constraintName != value)
        {
          _constraintName = value;
          this.OnPropertyChanged(this, new PropertyChangedEventArgs("ConstraintName"));
        }
      }
    }

		[Browsable(true)]
		[Category("Data")]
		[Description("The date that this object was created.")]
		[ReadOnlyAttribute(true)]
		public DateTime CreatedDate
		{
			get { return _createdDate; }
		}

		[Browsable(false)]
		public bool IsOneToOne
		{
			get
			{
				int matches = 0;
				Table childTable = (Table)this.ChildTableRef.Object;
				if (this.ColumnRelationships.Count == childTable.PrimaryKeyColumns.Count)
				{					
					foreach(Column column in childTable.PrimaryKeyColumns)
					{
						if (this.ColumnRelationships.GetByParentField(column) != null)
						{
							matches++;
						}
					}
					
				}

				return (matches == childTable.PrimaryKeyColumns.Count);
			}
		}

    #endregion

		#region Methods

		public bool IsPrimaryKeyRelation()
		{
			//Determine if this relation ship is based on primary keys
			bool retval = true;
			foreach (ColumnRelationship columnRelationship in this.ColumnRelationships)
			{
				Column parentColumn = (Column)columnRelationship.ParentColumnRef.Object;
				Table parentTable = (Table)this.ParentTableRef.Object;
				if (!parentTable.PrimaryKeyColumns.Contains(parentColumn))
					retval = false;
			}
			return retval;
		}

		public string ToLongString()
		{
			try
			{
				Column col1 = (Column)this.ColumnRelationships[0].ParentColumnRef.Object;
				Column col2 = (Column)this.ColumnRelationships[0].ChildColumnRef.Object;
				string retval = (this.RoleName == "" ? "" : this.RoleName + " ");
				retval += ((Table)col1.ParentTableRef.Object).Name + ".";
				retval += col1.ToString();
				retval += "->";
				retval += ((Table)col2.ParentTableRef.Object).Name + ".";
				retval += col2.ToString();
				return retval;
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public bool IsInvalidRelation()
		{
			if (this.ChildTableRef == null) return true;
			if (this.ChildTableRef.Object == null) return true;
			if (this.ParentTableRef == null) return true;
			if (this.ParentTableRef.Object == null) return true;
			return false;
		}

		public override bool Equals(object obj)
		{
			try
			{
				Relation relationOther = (Relation)obj;				

				if (this.IsInvalidRelation()) return false;
				if (relationOther.IsInvalidRelation()) return false;

				#region Check Parents
				string parentTableName1 = ((Table)this.ParentTableRef.Object).Name;
				string parentTableName2 = ((Table)relationOther.ParentTableRef.Object).Name;

				SortedDictionary<string, ColumnRelationship> list1 = new SortedDictionary<string, ColumnRelationship>();
				foreach (ColumnRelationship cr in this.ColumnRelationships)
				{
					if (cr.ChildColumnRef.Object != null)
						list1.Add(((Column)cr.ChildColumnRef.Object).Name, cr);
				}

				SortedDictionary<string, ColumnRelationship> list2 = new SortedDictionary<string, ColumnRelationship>();
				foreach (ColumnRelationship cr in relationOther.ColumnRelationships)
				{
					if (cr.ChildColumnRef.Object != null)
						list2.Add(((Column)cr.ChildColumnRef.Object).Name, cr);
				}

				string parentColName1 = "";
				foreach (string key in list1.Keys)
				{
					parentColName1 += key;
				}

				string parentColName2 = "";
				foreach (string key in list2.Keys)
				{
					parentColName2 += key;
				}
				#endregion

				#region Check Children
				string childTableName1 = ((Table)this.ChildTableRef.Object).Name;
				string childTableName2 = ((Table)relationOther.ChildTableRef.Object).Name;

				SortedDictionary<string, ColumnRelationship> list3 = new SortedDictionary<string, ColumnRelationship>();
				foreach (ColumnRelationship cr in this.ColumnRelationships)
				{
					if (cr.ParentColumnRef.Object != null)
						list3.Add(((Column)cr.ParentColumnRef.Object).Name, cr);
				}

				SortedDictionary<string, ColumnRelationship> list4 = new SortedDictionary<string, ColumnRelationship>();
				foreach (ColumnRelationship cr in relationOther.ColumnRelationships)
				{
					if (cr.ParentColumnRef.Object != null)
						list4.Add(((Column)cr.ParentColumnRef.Object).Name, cr);
				}

				string childColName1 = "";
				foreach (string key in list3.Keys)
				{
					childColName1 += key;
				}

				string childColName2 = "";
				foreach (string key in list4.Keys)
				{
					childColName2 += key;
				}
				#endregion

				//string parentCol
				return (parentTableName1 == parentTableName2) && 
					(parentColName1 == parentColName2) &&
					(childTableName1 == childTableName2) &&
					(childColName1 == childColName2);
			}
			catch (Exception ex)
			{				
				throw;
			}
			
		}

		#endregion

		#region IXMLable Members

		public override void XmlAppend(XmlNode node)
		{
			XmlDocument oDoc = node.OwnerDocument;

      XmlAttribute keyNode = oDoc.CreateAttribute("key");
      keyNode.Value = this.Key.ToString();
      node.Attributes.Append(keyNode);

      XmlNode columnRelationshipsNode = oDoc.CreateElement("columnRelationships"); 
			ColumnRelationships.XmlAppend(columnRelationshipsNode);
			node.AppendChild(columnRelationshipsNode);

			XmlNode childTableRefNode = oDoc.CreateElement("childTableRef");
      if (this.ChildTableRef != null)
        this.ChildTableRef.XmlAppend(childTableRefNode);
			node.AppendChild(childTableRefNode);

			XmlNode parentTableRefNode = oDoc.CreateElement("parentTableRef");
      if (this.ParentTableRef != null)
        this.ParentTableRef.XmlAppend(parentTableRefNode);
			node.AppendChild(parentTableRefNode);

			XmlAttribute id = oDoc.CreateAttribute("id");			
			id.Value = this.Id.ToString();
			node.Attributes.Append(id);

			XmlAttribute roleName = oDoc.CreateAttribute("roleName");			
			roleName.Value = this.RoleName;
			node.Attributes.Append(roleName);

      XmlAttribute constraintName = oDoc.CreateAttribute("constraintName");
      constraintName.Value = this.ConstraintName;
      node.Attributes.Append(constraintName);

			XmlAttribute created = oDoc.CreateAttribute("createdDate");
			created.Value = _createdDate.ToString("yyyy-MM-dd HH:mm:ss");
			node.Attributes.Append(created);

		}

    public override void XmlLoad(XmlNode node)
    {
      _key = XmlHelper.GetAttributeValue(node, "key", _key);

      XmlNode columnRelationshipsNode = node.SelectSingleNode("columnRelationships");
      ColumnRelationships.XmlLoad(columnRelationshipsNode);

      XmlNode childTableRefNode = node.SelectSingleNode("childTableRef");
      if (this.ChildTableRef == null) _childTableRef = new Reference(this.Root);
      this.ChildTableRef.XmlLoad(childTableRefNode);

      XmlNode parentTableRefNode = node.SelectSingleNode("parentTableRef");
      if (this.ParentTableRef == null) _parentTableRef = new Reference(this.Root);
      this.ParentTableRef.XmlLoad(parentTableRefNode);

      this.SetId(int.Parse(node.Attributes["id"].Value));

      this.RoleName = node.Attributes["roleName"].Value;
      this.ConstraintName = node.Attributes["constraintName"].Value;

			_createdDate = DateTime.ParseExact(XmlHelper.GetAttributeValue(node, "createdDate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);

      this.Dirty = false;
    }

		#endregion

		#region Helpers

		public Reference CreateRef()
		{
			Reference returnVal = new Reference(this.Root);
			returnVal.Ref = this.Id;
			returnVal.RefType = ReferenceType.Relation;
			return returnVal;
		}

		[Browsable(false)]
		public string PascalRoleName
		{
			get { return StringHelper.DatabaseNameToPascalCase(RoleName); }
		}

		[Browsable(false)]
		public string CamelRoleName
		{
			get { return StringHelper.DatabaseNameToCamelCase(RoleName); }
		}

		[Browsable(false)]
		public string DatabaseRoleName
		{
			get { return RoleName; }
		}

		[Browsable(false)]
		public ArrayList FkColumns
		{
      get
      {
        ArrayList fkColumns = new ArrayList();
        foreach (ColumnRelationship columnRel in this.ColumnRelationships)
        {
          fkColumns.Add(columnRel.ChildColumnRef.Object);
        }
        return fkColumns;
      }
		}

		protected internal void SetId(int id)
		{
			_id = id;
		}

		public override string ToString()
		{
			TableCollection tableCollection = ((ModelRoot)this.Root).Database.Tables;
			Table[] parentList = { };
			Table[] childList = { };
			if (this.ParentTableRef != null)
				parentList = tableCollection.GetById(this.ParentTableRef.Ref);
			if (this.ChildTableRef != null)
				childList = tableCollection.GetById(this.ChildTableRef.Ref);

			string retval = "";
			retval = (this.RoleName == "" ? "" : this.RoleName + " ") + "[" + ((parentList.Length == 0) ? "(Unknown)" : parentList[0].Name) + " -> " + ((childList.Length == 0) ? "(Unknown)" : childList[0].Name) + "]";
			return retval;
		}

    private void RefreshRoleName()
    {
      //try
      //{
      //  string newRoleName = "";
      //  if ((this.ParentTableRef != null) && (this.ChildTableRef != null))
      //  {
      //    newRoleName = ((Table)this.ParentTableRef.Object).Name + "_" + ((Table)this.ChildTableRef.Object).Name;
      //    Database database = ((ModelRoot)this.Root).Database;
      //    if (database.RelationRoleExists(newRoleName, this))
      //    {
      //      //If we are in there then need to loop and find a new name
      //      int ii = 1;
      //      while (database.RelationRoleExists(newRoleName + ii.ToString(), this))
      //        ii++;
      //      newRoleName = newRoleName + ii.ToString();
      //    }
      //  }
      //  this.RoleName = newRoleName;
      //}
      //catch (Exception ex)
      //{
      //  throw;
      //}
    }

		#endregion

	}
}
